package utils

import (
	"bytes"
	"errors"
	"fmt"
	"strconv"
	"time"

	"cashew.com/cashew_common/configs"
	"github.com/beego/beego/v2/core/logs"
	"github.com/go-redis/redis"
)

var (
	Redis    *redis.Client
	RedisExt *redis.Client
)

func NewRedisClient() *redis.Client {
	return RedisInit()
}
func NewRedisExtClient() *redis.Client {
	return RedisExtInit()
}

func RedisInit() *redis.Client {
	if Redis == nil {
		Redis = redis.NewClient(&redis.Options{
			Addr:     configs.RedisAddr,
			Password: configs.RedisPWD,
			DB:       configs.RedisDB,
		})
	}
	return Redis
}

func RedisExtInit() *redis.Client {
	if RedisExt == nil {
		RedisExt = redis.NewClient(&redis.Options{
			Addr:     configs.RedisExtAddr,
			Password: configs.RedisExtPWD,
			DB:       configs.RedisExtDB,
		})
	}
	return RedisExt
}

func RedisClose() {
	if Redis != nil {
		_ = Redis.Close()
		Redis = nil
	}
}

type RedisService struct {
}

func (r *RedisService) GetPipeline() redis.Pipeliner {
	pipe := Redis.Pipeline()
	return pipe
}

func (r *RedisService) Set(key string, value string, expiration time.Duration) {
	ret := Redis.Set(key, value, expiration)
	if ret.Err() != nil {
		logs.Error("redis set key:%s, value:%s, expiration:%d, ret:%v", key, value, expiration, ret.Err())
	}
}

func (r *RedisService) Get(key string) (string, error) {
	result, err := Redis.Get(key).Result()
	if err != nil && !errors.Is(err, redis.Nil) {
		// fmt
		return "", err
	}
	return result, nil
}

func (r *RedisService) SetExpire(key string, value string, expiration time.Duration) {
	Redis.Set(key, value, time.Second*expiration)
}
func (r *RedisService) SRandMember(key string) string {
	result := Redis.SRandMember(key)
	return result.Val()

}

func (r *RedisService) get(key string) string {
	result, err := Redis.Get(key).Result()
	if err != nil && !errors.Is(err, redis.Nil) {
		// fmt.Printf("获取缓存项 key=%v 失败: %v", key, err)
		return ""
	}

	if result == "" {
		// fmt.Printf("缓存项 key=%v 不存在", key)
		return ""
	}

	return result
}

func (r *RedisService) HGet(key string, field string) string {
	//ctx := context.Background()
	result, err := Redis.HGet(key, field).Result()
	if err != nil && !errors.Is(err, redis.Nil) {
		// fmt.Printf("获取缓存项 key=%v 失败: %v", key, err)
		return ""
	}

	if result == "" {
		// fmt.Printf("缓存项 key=%v 不存在", key)
		return ""
	}

	// fmt.Printf("缓存项 key=%v, value=%v", key, result)
	return result
}

func (r *RedisService) HGetAll(key string) (map[string]string, error) {
	result, err := Redis.HGetAll(key).Result()
	if err != nil {
		return nil, err
	}
	return result, nil
}

func (r *RedisService) HSet(key string, field string, value string) {
	Redis.HSet(key, field, value)
}

func (r *RedisService) HKeys(key string) ([]string, error) {
	result, err := Redis.HKeys(key).Result()
	if err != nil {
		return nil, err
	}
	return result, nil
}

func (r *RedisService) Expire(key string, expiration time.Duration) {
	Redis.Expire(key, expiration)
}
func (r *RedisService) Expire2Month(key string) {
	Redis.Expire(key, 60*60*24*6*30)
}

func (r *RedisService) GetFromIdsByStrategyPool(strategyId uint32) []string {
	id := int(strategyId)
	s1 := "app:strategy_user_pool:"
	s2 := strconv.Itoa(id)
	//定义Buffer类型
	var bt bytes.Buffer
	//向bt中写入字符串
	bt.WriteString(s1)
	bt.WriteString(s2)
	//获得拼接后的字符串
	key := bt.String()

	result, err := Redis.SMembers(key).Result()
	if err != nil && !errors.Is(err, redis.Nil) {
		//fmt.Printf("获取缓存项 key=%v 失败: %v", key, err)
		return nil
	}

	//fmt.Printf("缓存项 key=%v, value=%v", key, result)
	return result
}

func (r *RedisService) GetLabelIdsByStrategyId(strategyId uint32) []string {
	id := int(strategyId)
	s1 := "app:strategy_label_pool:"
	s2 := strconv.Itoa(id)
	//定义Buffer类型
	var bt bytes.Buffer
	//向bt中写入字符串
	bt.WriteString(s1)
	bt.WriteString(s2)
	//获得拼接后的字符串
	key := bt.String()

	result, err := Redis.SMembers(key).Result()
	if err != nil && !errors.Is(err, redis.Nil) {
		//fmt.Printf("获取缓存项 key=%v 失败: %v", key, err)
		return nil
	}

	return result
}

// LPush 将一个或多个值插入到列表头部
func (r *RedisService) LPush(key string, value string) error {
	return Redis.LPush(key, value).Err()
}

// RPush 将一个或多个值插入到列表尾部
func (r *RedisService) RPush(key string, value string) error {
	return Redis.RPush(key, value).Err()
}

// LPop 移除并返回列表头部的元素
func (r *RedisService) LPop(key string) (string, error) {
	result, err := Redis.LPop(key).Result()
	if err != nil && !errors.Is(err, redis.Nil) {
		return "", err
	}
	return result, nil
}

// RPop 移除并返回列表尾部的元素
func (r *RedisService) RPop(key string) (string, error) {
	result, err := Redis.RPop(key).Result()
	if err != nil && !errors.Is(err, redis.Nil) {
		return "", err
	}
	return result, nil
}

// LLen 获取列表的长度
func (r *RedisService) LLen(key string) (int64, error) {
	return Redis.LLen(key).Result()
}

// LRange获取列表中指定范围内的所有元素，start = 0, end = -1表示获取全部元素
func (r *RedisService) LRange(key string) ([]string, error) {
	result, err := Redis.LRange(key, 0, -1).Result()
	if err != nil {
		return nil, err
	}
	return result, nil
}

// LRangeN获取列表头部的n个元素
func (r *RedisService) LRangeN(key string, n int) ([]string, error) {
	result, err := Redis.LRange(key, 0, int64(n-1)).Result()
	if err != nil {
		return nil, err
	}
	return result, nil
}

func (r *RedisService) SAdd(key string, value string) error {
	return Redis.SAdd(key, value).Err()
}

func (r *RedisService) SRem(key string, value string) error {
	return Redis.SRem(key, value).Err()
}

func (r *RedisService) SIsMember(key string, value string) bool {
	return Redis.SIsMember(key, value).Val()
}

func (r *RedisService) SMembers(key string) ([]string, error) {
	result, err := Redis.SMembers(key).Result()
	if err != nil {
		return nil, err
	}
	return result, nil
}
func (r *RedisService) SCard(key string) (int64, error) {
	return Redis.SCard(key).Result()
}
func (r *RedisService) Incr(key string) error {
	return Redis.Incr(key).Err()
}
func (r *RedisService) IncrBy(key string, value int64) error {
	return Redis.IncrBy(key, value).Err()
}
func (r *RedisService) Decr(key string) error {
	return Redis.Decr(key).Err()
}
func (r *RedisService) DecrBy(key string, value int64) error {
	return Redis.DecrBy(key, value).Err()
}

func (r *RedisService) Scan(cursor uint64, match string, count int64) (keys []string, cursor2 uint64, err error) {
	return Redis.Scan(cursor, match, count).Result()
}

func (r *RedisService) HashExists(hashKey string) (bool, error) {
	// 使用 HLEN 命令检查 Hash 的长度
	length, err := Redis.HLen(hashKey).Result()
	if err != nil {
		return false, err
	}
	return length > 0, nil
}
func (r *RedisService) ZRevRangeWithScores(key string, start int64, end int64) ([]redis.Z, error) {
	return Redis.ZRevRangeWithScores(key, start, end).Result()
}
func (r *RedisService) ZAdd(key string, score float64, value string) error {
	return Redis.ZAdd(key, redis.Z{Score: score, Member: value}).Err()
}
func (r *RedisService) ZRem(key string, value ...string) error {
	return Redis.ZRem(key, value).Err()
}
func (r *RedisService) ZRangeByScore(key string, opt redis.ZRangeBy) ([]string, error) {
	return Redis.ZRangeByScore(key, opt).Result()
}
func (r *RedisService) ZRank(key string, value string) (int64, error) {
	return Redis.ZRank(key, value).Result()
}
func (r *RedisService) ZRevRank(key string, value string) (int64, error) {
	return Redis.ZRevRank(key, value).Result()
}
func (r *RedisService) ZScore(key string, value string) (float64, error) {
	return Redis.ZScore(key, value).Result()
}
func (r *RedisService) ZCount(key string, min string, max string) (int64, error) {
	return Redis.ZCount(key, min, max).Result()
}
func (r *RedisService) ZCard(key string) (int64, error) {
	return Redis.ZCard(key).Result()
}

func (r *RedisService) ExistsInZSet(key string, value string) bool {
	score, err := Redis.ZScore(key, value).Result()
	if err != nil {
		return false
	}
	return score > 0
}
func (r *RedisService) HashSet(hashKey string, field string, value string) error {
	return Redis.HSet(hashKey, field, value).Err()
}
func (r *RedisService) HashDel(hashKey string, field string) error {
	return Redis.HDel(hashKey, field).Err()
}
func (r *RedisService) IsHashKeyExists(prefix string) (bool, error) {
	var cursor uint64 = 0
	for {
		keys, nextCursor, err := Redis.Scan(cursor, prefix+"*", 100).Result()
		if err != nil {
			return false, err
		}
		cursor = nextCursor
		for _, key := range keys {
			length, err := Redis.HLen(key).Result()
			if err != nil {
				return false, err
			}
			if length > 0 {
				return true, nil // 修正逻辑错误，发现非空hash时返回true
			}
		}
		if cursor == 0 {
			break // 所有keys已遍历完毕
		}
	}
	return false, nil // 所有hash均为空时返回false
}

func (r *RedisService) Del(key ...string) error {
	return Redis.Del(key...).Err()
}

// 获取用户 call 锁方法
func (r *RedisService) AcquireCallUserLock(userId string, lockExpireTime int) (bool, error) {
	lockKey := fmt.Sprintf("CASHEW::CALLUSERLOCK::%s", userId)
	// 尝试获取锁，设置过期时间为30秒，使用SetNX命令保证只有键不存在时才能设置成功（即获取锁）
	if lockExpireTime <= 0 {
		// 默认锁过期时间为30秒
		lockExpireTime = 30
	}
	lockSuccess, err := Redis.SetNX(lockKey, 1, time.Duration(lockExpireTime)*time.Second).Result()
	if err != nil {
		return false, err
	}
	return lockSuccess, nil
}

func (r *RedisService) IsCallUserLockExists(userId string) (bool, error) {
	lockKey := fmt.Sprintf("CASHEW::CALLUSERLOCK::%s", userId)

	// 使用 GET 命令检查锁是否存在
	val, err := Redis.Get(lockKey).Result()
	if err != nil {
		// 如果键不存在，返回 false
		if err == redis.Nil {
			return false, nil
		}
		// 其他错误返回 false 和错误信息
		return false, err
	}

	// 如果值存在，返回 true
	return val != "", nil
}

// 删除用户 call 锁的方法
func (r *RedisService) ReleaseCallUserLock(userId string) error {
	lockKey := fmt.Sprintf("CASHEW::CALLUSERLOCK::%s", userId)
	_, err := Redis.Del(lockKey).Result()
	if err != nil {
		return err
	}

	return nil
}

// 重置用户 call 锁的方法
func (r *RedisService) ReSetCallUserLock(userId string, lockExpireTime int) error {
	// 先删除旧用户的锁
	err := r.ReleaseCallUserLock(userId)
	if err != nil {
		return err
	}

	// 再尝试获取新用户的锁
	_, err = r.AcquireCallUserLock(userId, lockExpireTime)
	if err != nil {
		return err
	}
	return nil
}

func (r *RedisService) ZRangeByScoreWithScores(key string, opt redis.ZRangeBy) ([]redis.Z, error) {
	result, err := Redis.ZRangeByScoreWithScores(key, opt).Result()
	if err != nil {
		return nil, err
	}
	return result, nil
}
